/*
 * Copyright (c) 2024-2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "reminder_store_strategy.h"

#include "ans_log_wrapper.h"
#include "reminder_table.h"
#include "reminder_request_alarm.h"
#include "reminder_request_timer.h"
#include "reminder_request_calendar.h"

namespace OHOS::Notification {
template<typename T>
static void GetRdbValue(const std::shared_ptr<NativeRdb::ResultSet>& resultSet,
    const std::string& name, T& value)
{
    int32_t columnIndex = -1;
    resultSet->GetColumnIndex(name, columnIndex);
    if (columnIndex == -1) {
        ANSR_LOGW("The column %{public}s does not exsit.", name.c_str());
        return;
    }

    if constexpr (std::is_same_v<T, std::string>) {
        resultSet->GetString(columnIndex, value);
    } else if constexpr (std::is_same_v<T, int64_t>) {
        resultSet->GetLong(columnIndex, value);
    } else if constexpr (std::is_same_v<T, uint64_t>) {
        int64_t t = 0;
        resultSet->GetLong(columnIndex, t);
        value = static_cast<uint64_t>(t);
    } else if constexpr (std::is_integral_v<T>) {
        int32_t t = 0;
        resultSet->GetInt(columnIndex, t);
        value = static_cast<T>(t);
    }
}

void ReminderBaseStrategy::StoreToRdb(const sptr<ReminderRequest>& reminder, NativeRdb::ValuesBucket& values)
{
    // already check the reminder not nullptr before calling
    values.PutInt(ReminderBaseTable::REMINDER_ID, reminder->GetReminderId());
    values.PutString(ReminderBaseTable::PACKAGE_NAME, reminder->GetBundleName());
    values.PutInt(ReminderBaseTable::USER_ID, reminder->GetUserId());
    values.PutInt(ReminderBaseTable::UID, reminder->GetUid());
    values.PutString(ReminderBaseTable::SYSTEM_APP, reminder->IsSystemApp() ? "true" : "false");
    values.PutInt(ReminderBaseTable::REMINDER_TYPE, static_cast<int32_t>(reminder->GetReminderType()));
    values.PutLong(ReminderBaseTable::REMINDER_TIME, reminder->GetReminderTimeInMilli());
    values.PutLong(ReminderBaseTable::TRIGGER_TIME, reminder->GetTriggerTimeInMilli());
    values.PutLong(ReminderBaseTable::TIME_INTERVAL, reminder->GetTimeInterval());
    values.PutInt(ReminderBaseTable::SNOOZE_TIMES, reminder->GetSnoozeTimes());
    values.PutInt(ReminderBaseTable::DYNAMIC_SNOOZE_TIMES, reminder->GetSnoozeTimesDynamic());
    values.PutLong(ReminderBaseTable::RING_DURATION, reminder->GetRingDuration());
    values.PutString(ReminderBaseTable::IS_EXPIRED, reminder->IsExpired() ? "true" : "false");
    values.PutInt(ReminderBaseTable::STATE, reminder->GetState());
    values.PutString(ReminderBaseTable::ACTION_BUTTON_INFO, reminder->SerializeButtonInfo());
    values.PutString(ReminderBaseTable::CUSTOM_BUTTON_URI, reminder->GetCustomButtonUri());
    values.PutInt(ReminderBaseTable::SLOT_ID, reminder->GetSlotType());
    values.PutInt(ReminderBaseTable::SNOOZE_SLOT_ID, reminder->GetSnoozeSlotType());
    values.PutInt(ReminderBaseTable::NOTIFICATION_ID, reminder->GetNotificationId());
    values.PutString(ReminderBaseTable::TITLE, reminder->GetTitle());
    values.PutInt(ReminderBaseTable::TITLE_RESOURCE_ID, reminder->GetTitleResourceId());
    values.PutString(ReminderBaseTable::CONTENT, reminder->GetContent());
    values.PutInt(ReminderBaseTable::CONTENT_RESOURCE_ID, reminder->GetContentResourceId());
    values.PutString(ReminderBaseTable::SNOOZE_CONTENT, reminder->GetSnoozeContent());
    values.PutInt(ReminderBaseTable::SNOOZE_CONTENT_RESOURCE_ID, reminder->GetSnoozeContentResourceId());
    values.PutString(ReminderBaseTable::EXPIRED_CONTENT, reminder->GetExpiredContent());
    values.PutInt(ReminderBaseTable::EXPIRED_CONTENT_RESOURCE_ID, reminder->GetExpiredContentResourceId());

    std::string wantInfoStr;
    std::string maxWantInfoStr;
    reminder->SerializeWantAgent(wantInfoStr, maxWantInfoStr);
    values.PutString(ReminderBaseTable::WANT_AGENT, wantInfoStr);
    values.PutString(ReminderBaseTable::MAX_SCREEN_WANT_AGENT, maxWantInfoStr);
    
    values.PutString(ReminderBaseTable::TAP_DISMISSED, reminder->IsTapDismissed() ? "true" : "false");
    values.PutLong(ReminderBaseTable::AUTO_DELETED_TIME, reminder->GetAutoDeletedTime());
    values.PutString(ReminderBaseTable::GROUP_ID, reminder->GetGroupId());
    values.PutString(ReminderBaseTable::CUSTOM_RING_URI, reminder->GetCustomRingUri());
    values.PutInt(ReminderBaseTable::RING_CHANNEL, static_cast<int32_t>(reminder->GetRingChannel()));
    values.PutString(ReminderBaseTable::CREATOR_BUNDLE_NAME, reminder->GetCreatorBundleName());
    values.PutInt(ReminderBaseTable::CREATOR_UID, reminder->GetCreatorUid());
}

void ReminderBaseStrategy::RestoreFromRdb(sptr<ReminderRequest>& reminder,
    const std::shared_ptr<NativeRdb::ResultSet>& results)
{
    // already check the reminder and the results not nullptr before calling
    RestoreTime(reminder, results);
    RestoreId(reminder, results);
    RestoreContext(reminder, results);

    uint8_t state = 0;
    GetRdbValue<uint8_t>(results, ReminderBaseTable::STATE, state);
    reminder->SetState(state);

    std::string isSystemApp;
    GetRdbValue<std::string>(results, ReminderBaseTable::SYSTEM_APP, isSystemApp);
    reminder->SetSystemApp(isSystemApp == "true" ? true : false);

    std::string isExpired;
    GetRdbValue<std::string>(results, ReminderBaseTable::IS_EXPIRED, isExpired);
    reminder->SetExpired(isExpired == "true" ? true : false);

    std::string actionButtons;
    GetRdbValue<std::string>(results, ReminderBaseTable::ACTION_BUTTON_INFO, actionButtons);
    reminder->DeserializeButtonInfo(actionButtons);

    std::string wantAgent;
    GetRdbValue<std::string>(results, ReminderBaseTable::WANT_AGENT, wantAgent);
    reminder->DeserializeWantAgent(wantAgent, 0);

    std::string maxWantAgent;
    GetRdbValue<std::string>(results, ReminderBaseTable::MAX_SCREEN_WANT_AGENT, maxWantAgent);
    reminder->DeserializeWantAgent(maxWantAgent, 1);

    std::string tapDismissed;
    GetRdbValue<std::string>(results, ReminderBaseTable::TAP_DISMISSED, tapDismissed);
    reminder->SetTapDismissed(tapDismissed == "true" ? true : false);

    int32_t ringChannel = 0;
    GetRdbValue<int32_t>(results, ReminderBaseTable::RING_CHANNEL, ringChannel);
    reminder->SetRingChannel(static_cast<ReminderRequest::RingChannel>(ringChannel));
}

void ReminderBaseStrategy::RestoreTime(sptr<ReminderRequest>& reminder,
    const std::shared_ptr<NativeRdb::ResultSet>& results)
{
    uint64_t reminderTime = 0;
    GetRdbValue<uint64_t>(results, ReminderBaseTable::REMINDER_TIME, reminderTime);
    reminder->SetReminderTimeInMilli(reminderTime);

    uint64_t triggerTime = 0;
    GetRdbValue<uint64_t>(results, ReminderBaseTable::TRIGGER_TIME, triggerTime);
    reminder->SetTriggerTimeInMilli(triggerTime);

    uint64_t timeInterval = 0;
    GetRdbValue<uint64_t>(results, ReminderBaseTable::TIME_INTERVAL, timeInterval);
    reminder->SetTimeInterval(timeInterval);

    uint8_t snoozeTimes = 0;
    GetRdbValue<uint8_t>(results, ReminderBaseTable::SNOOZE_TIMES, snoozeTimes);
    reminder->SetSnoozeTimes(snoozeTimes);

    uint8_t dynamicSnoozeTimes = 0;
    GetRdbValue<uint8_t>(results, ReminderBaseTable::DYNAMIC_SNOOZE_TIMES, dynamicSnoozeTimes);
    reminder->SetSnoozeTimesDynamic(dynamicSnoozeTimes);

    uint64_t ringDuration = 0;
    GetRdbValue<uint64_t>(results, ReminderBaseTable::RING_DURATION, ringDuration);
    reminder->SetRingDuration(ringDuration);

    int64_t autoDeletedTime = 0;
    GetRdbValue<int64_t>(results, ReminderBaseTable::AUTO_DELETED_TIME, autoDeletedTime);
    reminder->SetAutoDeletedTime(autoDeletedTime);
}

void ReminderBaseStrategy::RestoreId(sptr<ReminderRequest>& reminder,
    const std::shared_ptr<NativeRdb::ResultSet>& results)
{
    int32_t reminderId = 0;
    GetRdbValue<int32_t>(results, ReminderBaseTable::REMINDER_ID, reminderId);
    reminder->SetReminderId(reminderId);

    int32_t userId = 0;
    GetRdbValue<int32_t>(results, ReminderBaseTable::USER_ID, userId);
    reminder->SetUserId(userId);

    int32_t uid = 0;
    GetRdbValue<int32_t>(results, ReminderBaseTable::UID, uid);
    reminder->SetUid(uid);

    int32_t slotType = 0;
    GetRdbValue<int32_t>(results, ReminderBaseTable::SLOT_ID, slotType);
    reminder->SetSlotType(NotificationConstant::SlotType(slotType));

    int32_t snoozeSlotType = 0;
    GetRdbValue<int32_t>(results, ReminderBaseTable::SNOOZE_SLOT_ID, snoozeSlotType);
    reminder->SetSnoozeSlotType(NotificationConstant::SlotType(snoozeSlotType));

    int32_t notificationId = 0;
    GetRdbValue<int32_t>(results, ReminderBaseTable::NOTIFICATION_ID, notificationId);
    reminder->SetNotificationId(notificationId);

    std::string groupId;
    GetRdbValue<std::string>(results, ReminderBaseTable::GROUP_ID, groupId);
    reminder->SetGroupId(groupId);

    int32_t creatorUid = 0;
    GetRdbValue<int32_t>(results, ReminderBaseTable::CREATOR_UID, creatorUid);
    reminder->SetCreatorUid(creatorUid);

    int32_t titleResourceId = 0;
    GetRdbValue<int32_t>(results, ReminderBaseTable::TITLE_RESOURCE_ID, titleResourceId);
    reminder->SetTitleResourceId(titleResourceId);

    int32_t contentResourceId = 0;
    GetRdbValue<int32_t>(results, ReminderBaseTable::CONTENT_RESOURCE_ID, contentResourceId);
    reminder->SetContentResourceId(contentResourceId);

    int32_t snoozeContentResourceId = 0;
    GetRdbValue<int32_t>(results, ReminderBaseTable::SNOOZE_CONTENT_RESOURCE_ID, snoozeContentResourceId);
    reminder->SetSnoozeContentResourceId(snoozeContentResourceId);

    int32_t expiredContentResourceId = 0;
    GetRdbValue<int32_t>(results, ReminderBaseTable::EXPIRED_CONTENT_RESOURCE_ID, expiredContentResourceId);
    reminder->SetExpiredContentResourceId(expiredContentResourceId);
}

void ReminderBaseStrategy::RestoreContext(sptr<ReminderRequest>& reminder,
    const std::shared_ptr<NativeRdb::ResultSet>& results)
{
    std::string bundleName;
    GetRdbValue<std::string>(results, ReminderBaseTable::PACKAGE_NAME, bundleName);
    reminder->SetBundleName(bundleName);

    std::string customButtonUri;
    GetRdbValue<std::string>(results, ReminderBaseTable::CUSTOM_BUTTON_URI, customButtonUri);
    reminder->SetCustomButtonUri(customButtonUri);

    std::string title;
    GetRdbValue<std::string>(results, ReminderBaseTable::TITLE, title);
    reminder->SetTitle(title);

    std::string content;
    GetRdbValue<std::string>(results, ReminderBaseTable::CONTENT, content);
    reminder->SetContent(content);

    std::string snoozeContent;
    GetRdbValue<std::string>(results, ReminderBaseTable::SNOOZE_CONTENT, snoozeContent);
    reminder->SetSnoozeContent(snoozeContent);

    std::string expiredContent;
    GetRdbValue<std::string>(results, ReminderBaseTable::EXPIRED_CONTENT, expiredContent);
    reminder->SetExpiredContent(expiredContent);

    std::string customRingUri;
    GetRdbValue<std::string>(results, ReminderBaseTable::CUSTOM_RING_URI, customRingUri);
    reminder->SetCustomRingUri(customRingUri);

    std::string creatorBundleName;
    GetRdbValue<std::string>(results, ReminderBaseTable::CREATOR_BUNDLE_NAME, creatorBundleName);
    reminder->SetCreatorBundleName(creatorBundleName);
}

void ReminderTimerStrategy::StoreToRdb(const sptr<ReminderRequest>& reminder, NativeRdb::ValuesBucket& baseValues,
    NativeRdb::ValuesBucket& values)
{
    if (reminder == nullptr || reminder->GetReminderType() != ReminderRequest::ReminderType::TIMER) {
        return;
    }
    // already check the reminder not nullptr and type is timer before calling
    ReminderBaseStrategy::StoreToRdb(reminder, baseValues);
    ReminderRequestTimer* timer = static_cast<ReminderRequestTimer*>(reminder.GetRefPtr());
    values.PutInt(ReminderTimerTable::REMINDER_ID, reminder->GetReminderId());
    values.PutLong(ReminderTimerTable::TRIGGER_SECOND, timer->GetInitInfo());
    values.PutLong(ReminderTimerTable::START_DATE_TIME, 0);
    values.PutLong(ReminderTimerTable::END_DATE_TIME, 0);
}

sptr<ReminderRequest> ReminderTimerStrategy::RestoreFromRdb(const std::shared_ptr<NativeRdb::ResultSet>& baseResults,
    const std::shared_ptr<NativeRdb::ResultSet>& results)
{
    if (baseResults == nullptr || results == nullptr) {
        ANSR_LOGW("ResultSet is nullptr.");
        return nullptr;
    }
    sptr<ReminderRequest> reminder = sptr<ReminderRequestTimer>::MakeSptr(ReminderRequest::ReminderType::TIMER);
    ReminderBaseStrategy::RestoreFromRdb(reminder, baseResults);
    ReminderRequestTimer* timer = static_cast<ReminderRequestTimer*>(reminder.GetRefPtr());

    uint64_t seconds = 0;
    GetRdbValue<uint64_t>(results, ReminderTimerTable::TRIGGER_SECOND, seconds);
    timer->SetInitInfo(seconds);
    return reminder;
}

std::string ReminderTimerStrategy::GetTableName()
{
    return ReminderTimerTable::TABLE_NAME;
}

std::string ReminderTimerStrategy::GetTableColumns()
{
    return ReminderTimerTable::SELECT_COLUMNS;
}

void ReminderAlarmStrategy::StoreToRdb(const sptr<ReminderRequest>& reminder, NativeRdb::ValuesBucket& baseValues,
    NativeRdb::ValuesBucket& values)
{
    if (reminder == nullptr || reminder->GetReminderType() != ReminderRequest::ReminderType::ALARM) {
        return;
    }
    // already check the reminder not nullptr and type is alarm before calling
    ReminderBaseStrategy::StoreToRdb(reminder, baseValues);
    ReminderRequestAlarm* alarm = static_cast<ReminderRequestAlarm*>(reminder.GetRefPtr());
    values.PutInt(ReminderAlarmTable::REMINDER_ID, reminder->GetReminderId());
    values.PutInt(ReminderAlarmTable::ALARM_HOUR, alarm->GetHour());
    values.PutInt(ReminderAlarmTable::ALARM_MINUTE, alarm->GetMinute());
    values.PutInt(ReminderAlarmTable::REPEAT_DAYS_OF_WEEK, alarm->GetRepeatDaysOfWeek());
}

sptr<ReminderRequest> ReminderAlarmStrategy::RestoreFromRdb(const std::shared_ptr<NativeRdb::ResultSet>& baseResults,
    const std::shared_ptr<NativeRdb::ResultSet>& results)
{
    if (baseResults == nullptr || results == nullptr) {
        ANSR_LOGW("ResultSet is nullptr.");
        return nullptr;
    }
    sptr<ReminderRequest> reminder = sptr<ReminderRequestAlarm>::MakeSptr(ReminderRequest::ReminderType::ALARM);
    ReminderBaseStrategy::RestoreFromRdb(reminder, baseResults);
    ReminderRequestAlarm* alarm = static_cast<ReminderRequestAlarm*>(reminder.GetRefPtr());

    uint8_t hour = 0;
    GetRdbValue<uint8_t>(results, ReminderAlarmTable::ALARM_HOUR, hour);
    alarm->SetHour(hour);

    uint8_t minute = 0;
    GetRdbValue<uint8_t>(results, ReminderAlarmTable::ALARM_MINUTE, minute);
    alarm->SetMinute(minute);

    uint8_t repeatDaysOfWeek = 0;
    GetRdbValue<uint8_t>(results, ReminderAlarmTable::REPEAT_DAYS_OF_WEEK, repeatDaysOfWeek);
    alarm->SetRepeatDaysOfWeek(repeatDaysOfWeek);
    return reminder;
}

std::string ReminderAlarmStrategy::GetTableName()
{
    return ReminderAlarmTable::TABLE_NAME;
}

std::string ReminderAlarmStrategy::GetTableColumns()
{
    return ReminderAlarmTable::SELECT_COLUMNS;
}

void ReminderCalendarStrategy::StoreToRdb(const sptr<ReminderRequest>& reminder, NativeRdb::ValuesBucket& baseValues,
    NativeRdb::ValuesBucket& values)
{
    if (reminder == nullptr || reminder->GetReminderType() != ReminderRequest::ReminderType::CALENDAR) {
        return;
    }
    // already check the reminder not nullptr and type is calendar before calling
    ReminderBaseStrategy::StoreToRdb(reminder, baseValues);
    ReminderRequestCalendar* calendar = static_cast<ReminderRequestCalendar*>(reminder.GetRefPtr());
    values.PutInt(ReminderCalendarTable::REMINDER_ID, reminder->GetReminderId());
    values.PutInt(ReminderCalendarTable::FIRST_DESIGNATE_YEAR, calendar->GetFirstDesignateYear());
    values.PutInt(ReminderCalendarTable::FIRST_DESIGNATE_MONTH, calendar->GetFirstDesignageMonth());
    values.PutInt(ReminderCalendarTable::FIRST_DESIGNATE_DAY, calendar->GetFirstDesignateDay());
    values.PutLong(ReminderCalendarTable::CALENDAR_DATE_TIME, calendar->GetDateTime());
    values.PutLong(ReminderCalendarTable::CALENDAR_END_DATE_TIME, calendar->GetEndDateTime());
    values.PutLong(ReminderCalendarTable::CALENDAR_LAST_DATE_TIME, calendar->GetLastStartDateTime());
    values.PutInt(ReminderCalendarTable::REPEAT_DAYS, calendar->GetRepeatDay());
    values.PutInt(ReminderCalendarTable::REPEAT_MONTHS, calendar->GetRepeatMonth());
    values.PutInt(ReminderCalendarTable::REPEAT_DAYS_OF_WEEK, calendar->GetRepeatDaysOfWeek());
    values.PutString(ReminderCalendarTable::RRULE_WANT_AGENT, calendar->SerializationRRule());
    values.PutString(ReminderCalendarTable::EXCLUDE_DATES, calendar->SerializationExcludeDates());
}

sptr<ReminderRequest> ReminderCalendarStrategy::RestoreFromRdb(const std::shared_ptr<NativeRdb::ResultSet>& baseResults,
    const std::shared_ptr<NativeRdb::ResultSet>& results)
{
    if (baseResults == nullptr || results == nullptr) {
        ANSR_LOGW("ResultSet is nullptr.");
        return nullptr;
    }
    sptr<ReminderRequest> reminder = sptr<ReminderRequestCalendar>::MakeSptr(ReminderRequest::ReminderType::CALENDAR);
    ReminderBaseStrategy::RestoreFromRdb(reminder, baseResults);
    RestoreTime(reminder, results);
    ReminderRequestCalendar* calendar = static_cast<ReminderRequestCalendar*>(reminder.GetRefPtr());

    uint32_t repeatDay = 0;
    GetRdbValue<uint32_t>(results, ReminderCalendarTable::REPEAT_DAYS, repeatDay);
    calendar->SetRepeatDay(repeatDay);

    uint16_t repeatMonth = 0;
    GetRdbValue<uint16_t>(results, ReminderCalendarTable::REPEAT_MONTHS, repeatMonth);
    calendar->SetRepeatMonth(repeatMonth);

    uint8_t repeatDaysOfWeek = 0;
    GetRdbValue<uint8_t>(results, ReminderCalendarTable::REPEAT_DAYS_OF_WEEK, repeatDaysOfWeek);
    calendar->SetRepeatDaysOfWeek(repeatDaysOfWeek);

    std::string rruleWantAgent;
    GetRdbValue<std::string>(results, ReminderCalendarTable::RRULE_WANT_AGENT, rruleWantAgent);
    calendar->DeserializationRRule(rruleWantAgent);

    std::string excludeDates;
    GetRdbValue<std::string>(results, ReminderCalendarTable::EXCLUDE_DATES, excludeDates);
    calendar->DeserializationExcludeDates(excludeDates);
    return reminder;
}

std::string ReminderCalendarStrategy::GetTableName()
{
    return ReminderCalendarTable::TABLE_NAME;
}

std::string ReminderCalendarStrategy::GetTableColumns()
{
    return ReminderCalendarTable::SELECT_COLUMNS;
}

void ReminderCalendarStrategy::RestoreTime(sptr<ReminderRequest>& reminder,
    const std::shared_ptr<NativeRdb::ResultSet>& results)
{
    // already check the reminder and the results not nullptr before calling
    ReminderRequestCalendar* calendar = static_cast<ReminderRequestCalendar*>(reminder.GetRefPtr());

    uint16_t firstDesignateYear = 0;
    GetRdbValue<uint16_t>(results, ReminderCalendarTable::FIRST_DESIGNATE_YEAR, firstDesignateYear);
    calendar->SetFirstDesignateYear(firstDesignateYear);

    uint8_t firstDesignateMonth = 0;
    GetRdbValue<uint8_t>(results, ReminderCalendarTable::FIRST_DESIGNATE_MONTH, firstDesignateMonth);
    calendar->SetFirstDesignageMonth(firstDesignateMonth);

    uint8_t firstDesignateDay = 0;
    GetRdbValue<uint8_t>(results, ReminderCalendarTable::FIRST_DESIGNATE_DAY, firstDesignateDay);
    calendar->SetFirstDesignateDay(firstDesignateDay);

    uint64_t dateTime = 0;
    GetRdbValue<uint64_t>(results, ReminderCalendarTable::CALENDAR_DATE_TIME, dateTime);
    calendar->SetDateTime(dateTime);

    uint64_t endDateTime = 0;
    GetRdbValue<uint64_t>(results, ReminderCalendarTable::CALENDAR_END_DATE_TIME, endDateTime);
    if (endDateTime != 0 && endDateTime >= dateTime) {
        calendar->SetEndDateTime(endDateTime);
    } else {
        calendar->SetEndDateTime(dateTime);
    }

    uint64_t lastStartDateTime = 0;
    GetRdbValue<uint64_t>(results, ReminderCalendarTable::CALENDAR_LAST_DATE_TIME, lastStartDateTime);
    if (lastStartDateTime == 0) {
        calendar->SetLastStartDateTime(dateTime);
    } else {
        calendar->SetLastStartDateTime(lastStartDateTime);
    }
}
}